package com.wyl.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.wyl.rbac.service.UsersService;

/**
 * 安全配置类
 * @className: SecurityConfig
 * @date: 2016年5月4日 下午6:17:45
 * @author sunkxu
 * @version 0.1
 * @since JDK 1.8
 */
@Configuration
@EnableWebSecurity
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityConfig {

	private static final String[] UNSECURED_RESOURCE_LIST = new String[] { "/resources/**", "/assets/**", "/css/**",
			"/fonts/**", "/images/**", "/js/**" };

	private static final String[] UNAUTHORIZED_RESOURCE_LIST = new String[] { "/test.html","/welcome*", "/", "/unauthorized*",
			"/error*", "/users*", "/accessDenied" };


	@Configuration
	@Profile({ "dev", "test", "pro" })
	protected static class JdbcPersistentTokenRememberMeSetup {
		@Value("${rememberMeToken}")
		private String rememberMeToken;

		@Value("${rememberMeParameter}")
		private String rememberMeParameter;

		@Autowired
		private DataSource dataSource;

		@Bean
		public RememberMeServices getRememberMeServices() {
			/*JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
			jdbcUserDetailsManager.setDataSource(dataSource);*/			
			UsersService usersService = new UsersService();

			JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();
			jdbcTokenRepositoryImpl.setDataSource(dataSource);

			PersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices(
					rememberMeToken, usersService, jdbcTokenRepositoryImpl);
			services.setParameter(rememberMeParameter);
			return services;
		}
	}

	/*@Order(Ordered.HIGHEST_PRECEDENCE)
	@Configuration
	protected static class ExternalAuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {
		@Autowired
		private DataSource dataSource;

		@Override
		public void init(AuthenticationManagerBuilder auth) throws Exception {
			//@formatter:off
			String authoritiesByUsernameQuery = "select username, authority from user_authorities " +
					"inner join users on user_authorities.user_id = users.id " +
					"inner join authorities on user_authorities.authority_id = authorities.id " +
					"where username = ?";

			JdbcUserDetailsManager userDetailsService = new JdbcUserDetailsManager();
			userDetailsService.setDataSource(dataSource);
			userDetailsService.setAuthoritiesByUsernameQuery(authoritiesByUsernameQuery);
			PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

			auth
				.userDetailsService(userDetailsService)
					.passwordEncoder(passwordEncoder)
				.and()
					.jdbcAuthentication()
						.authoritiesByUsernameQuery(authoritiesByUsernameQuery)
						.passwordEncoder(passwordEncoder)
						.dataSource(dataSource)
			;
			//@formatter:on
		}
	}*/

	@Configuration
	@Order(1)
	@Profile({ "pro" })
	public static class ProWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
		@Value("${rememberMeToken}")
		private String rememberMeToken;

		@Value("${spring.profiles.active}")
		private String activeProfile;

		@Autowired
		RememberMeServices rememberMeServices;
		
		@Override
		@Bean
		public UserDetailsService userDetailsService() {
			return new UsersService();
		}

		@Override
		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
			auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
		}

		@Override
		public void configure(WebSecurity web) throws Exception {
			//@formatter:off
			web
				.ignoring()
					.antMatchers(UNSECURED_RESOURCE_LIST);
			//@formatter:on
		}

		@Override
		protected void configure(HttpSecurity http) throws Exception {
			//@formatter:off
			
			http
				.headers()
					.frameOptions()
						.sameOrigin()
			.and()
				.authorizeRequests()
					.antMatchers(UNAUTHORIZED_RESOURCE_LIST)
						.permitAll()
					.antMatchers("/git", "/manage", "/manage/**")
						.hasRole("ADMIN")
					.anyRequest()
						.authenticated()
			.and()
				.formLogin()
					.loginPage("/login")
					.permitAll()
			.and()
				.headers()
					.cacheControl()
				.and()
					.frameOptions()
						.deny()
			.and()
				.exceptionHandling()
					.accessDeniedPage("/access?error")
			.and()
				.rememberMe()
					.useSecureCookie(true)
					.tokenValiditySeconds(60 * 60 * 24 * 10) // 10 days
					.rememberMeServices(rememberMeServices)
					.key(rememberMeToken)
			.and()
				.logout()
					.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
					.logoutSuccessUrl("/?logout")
			.and()
				.sessionManagement()
					.maximumSessions(1)
					.expiredUrl("/login?expired");
			// @formatter:on
		}
	}

	@Configuration
	@Order(1)
	@Profile({ "dev", "test" })
	public static class NonProWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
		@Value("${rememberMeToken}")
		private String rememberMeToken;

		@Value("${spring.profiles.active}")
		private String activeProfile;

		@Autowired
		RememberMeServices rememberMeServices;

		@Override
		@Bean
		public UserDetailsService userDetailsService() {
			return new UsersService();
		}

		@Override
		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
			auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
		}
		
		@Override
		public void configure(WebSecurity web) throws Exception {
			//@formatter:off
			web
				.ignoring()
					.antMatchers(UNSECURED_RESOURCE_LIST);
			//@formatter:on
		}

		@Override
		protected void configure(HttpSecurity http) throws Exception {
			//@formatter:off
			http
				.headers()
					.frameOptions()
						.sameOrigin()
			.and()
				.authorizeRequests()
					.antMatchers(UNAUTHORIZED_RESOURCE_LIST)
						.permitAll()
					.antMatchers("/git", "/manage", "/manage/**")
						.permitAll()
					.anyRequest()
						.authenticated()
			.and()
				.formLogin()
					.loginPage("/login")
					.permitAll()
			.and()
				.headers()
					.cacheControl()
				.and()
					.frameOptions()
						.deny()
			.and()
				.exceptionHandling()
					.accessDeniedPage("/access?error")
			.and()
				.rememberMe()
					.useSecureCookie(true)
					.tokenValiditySeconds(60 * 60 * 24 * 10) // 10 days
					.rememberMeServices(rememberMeServices)
					.key(rememberMeToken)
			.and()
				.logout()
					.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
					.logoutSuccessUrl("/?logout")
			.and()
				.sessionManagement()
					.maximumSessions(1)
					.expiredUrl("/login?expired");
			// @formatter:on
		}
	}

	// Register HttpSessionEventPublisher
	@Bean
	public static ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
		return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
	}
}
